package net.xo68.destiny.starter.worker.logger;

import lombok.extern.slf4j.Slf4j;
import net.xo68.destiny.core.index.bplustree.BPlusTree;
import net.xo68.destiny.core.index.bplustree.BPlusTreeIndexEntity;
import net.xo68.destiny.core.util.DiskUtils;
import net.xo68.destiny.core.util.TimeUtils;
import net.xo68.destiny.starter.worker.SchedulerWorkerProperties;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.ByteBuffer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Component
public class JobLoggerIndex implements SmartInitializingSingleton, DisposableBean {

    public static final String INDEX_FILE_NAME="/index.db";

    /**
     * 初始化
     */
    public static final int STATE_INIT=0;
    /**
     * 已经启动
     */
    public static final int STATE_STARTED=1;
    /**
     * 关闭的
     */
    public static final int STATE_SHUTDOWN=2;

    private final BPlusTree<Long,Long> bPlusTreeIndexs;

    /**
     * 状态
     */
    private final AtomicInteger state=new AtomicInteger(STATE_INIT);

    private final SchedulerWorkerProperties schedulerWorkerProperties;

    /**
     * 索引锁
     */
    private final ReentrantLock indexLock=new ReentrantLock();

    public JobLoggerIndex(SchedulerWorkerProperties schedulerWorkerProperties) {
        this.schedulerWorkerProperties = schedulerWorkerProperties;
        this.bPlusTreeIndexs= new BPlusTree<>(3);
    }

    public Long getDayIndex(long instanceId){
        try {
            indexLock.lock();
            Long dayIndex=bPlusTreeIndexs.get(instanceId);
            if(dayIndex==null){
                dayIndex=TimeUtils.nowDay();
                bPlusTreeIndexs.insertOrUpdate(instanceId, dayIndex);
                saveIndex();

            }
            return dayIndex;
        }finally {
            indexLock.unlock();
        }
    }

    @Override
    public void destroy() throws Exception {
        if(this.state.compareAndSet(STATE_STARTED,STATE_SHUTDOWN)){
            //todo destroy
        }
    }

    private String indexFilePath(){
        return this.schedulerWorkerProperties.loggerPath()+INDEX_FILE_NAME;
    }

    @Override
    public void afterSingletonsInstantiated() {
        switch (this.state.get()){
            case STATE_INIT:
                if(this.state.compareAndSet(STATE_INIT,STATE_STARTED)){
                    //todo init
                    loadIndex();


                }
                break;
            case STATE_STARTED:
            case STATE_SHUTDOWN:
                break;
            default:
                throw new Error("Invalid State");
        }
    }

    private void loadIndex(){
        try {
            indexLock.lock();
            ByteBuffer byteBuffer= DiskUtils.read(new File(indexFilePath()));
            if(byteBuffer!=null){
                while (byteBuffer.hasRemaining()){
                    bPlusTreeIndexs.insertOrUpdate(byteBuffer.getLong(),byteBuffer.getLong());
                }
                //final List<BPlusTreeIndexEntity<Long,Long>> indexEntities= new LinkedList<>();
                //bPlusTreeIndexs.fillToList(indexEntities);
                //log.info("indexEntities: {}", indexEntities);
            }
        }finally {
            indexLock.unlock();
        }
    }

    private void saveIndex(){
        final List<BPlusTreeIndexEntity<Long,Long>> indexEntities= new LinkedList<>();
        bPlusTreeIndexs.fillToList(indexEntities);
        final ByteBuffer byteBuffer=ByteBuffer.allocate(indexEntities.size() * 16);
        Iterator<BPlusTreeIndexEntity<Long, Long>> iter = indexEntities.iterator();
        while (iter.hasNext()){
            BPlusTreeIndexEntity<Long, Long> idxEntry = iter.next();
            byteBuffer.putLong(idxEntry.getIndex());
            byteBuffer.putLong(idxEntry.getValue());
        }
        byteBuffer.flip();
        DiskUtils.write(new File(indexFilePath()),byteBuffer);
    }
}
